using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text;

namespace WinEnumerator
{
	/// <summary>
	/// Window Style Flags
	/// </summary>
	[Flags]
	public enum WindowStyleFlags : uint
	{
		WS_OVERLAPPED      = 0x00000000,
		WS_POPUP           = 0x80000000,
		WS_CHILD           = 0x40000000,
		WS_MINIMIZE        = 0x20000000,
		WS_VISIBLE         = 0x10000000,
		WS_DISABLED        = 0x08000000,
		WS_CLIPSIBLINGS    = 0x04000000,
		WS_CLIPCHILDREN    = 0x02000000,
		WS_MAXIMIZE        = 0x01000000,
		WS_BORDER          = 0x00800000,
		WS_DLGFRAME        = 0x00400000,
		WS_VSCROLL         = 0x00200000,
		WS_HSCROLL         = 0x00100000,
		WS_SYSMENU         = 0x00080000,
		WS_THICKFRAME      = 0x00040000,
		WS_GROUP           = 0x00020000,
		WS_TABSTOP         = 0x00010000,
		WS_MINIMIZEBOX     = 0x00020000,
		WS_MAXIMIZEBOX     = 0x00010000,
	}
	
	/// <summary>
	/// Extended Windows Style flags
	/// </summary>
	[Flags]
	public enum ExtendedWindowStyleFlags : int
	{
		 WS_EX_DLGMODALFRAME    = 0x00000001,
		 WS_EX_NOPARENTNOTIFY   = 0x00000004,
		 WS_EX_TOPMOST          = 0x00000008,
		 WS_EX_ACCEPTFILES      = 0x00000010,
		 WS_EX_TRANSPARENT      = 0x00000020,

		 WS_EX_MDICHILD         = 0x00000040,
		 WS_EX_TOOLWINDOW       = 0x00000080,
		 WS_EX_WINDOWEDGE       = 0x00000100,
		 WS_EX_CLIENTEDGE       = 0x00000200,
		 WS_EX_CONTEXTHELP      = 0x00000400,

		 WS_EX_RIGHT            = 0x00001000,
		 WS_EX_LEFT             = 0x00000000,
		 WS_EX_RTLREADING       = 0x00002000,
		 WS_EX_LTRREADING       = 0x00000000,
		 WS_EX_LEFTSCROLLBAR    = 0x00004000,
		 WS_EX_RIGHTSCROLLBAR   = 0x00000000,

		 WS_EX_CONTROLPARENT    = 0x00010000,
		 WS_EX_STATICEDGE       = 0x00020000,
		 WS_EX_APPWINDOW        = 0x00040000,

		 WS_EX_LAYERED          = 0x00080000,

		 WS_EX_NOINHERITLAYOUT  = 0x00100000, // Disable inheritence of mirroring by children
		 WS_EX_LAYOUTRTL        = 0x00400000, // Right to left mirroring

		 WS_EX_COMPOSITED       = 0x02000000,
		 WS_EX_NOACTIVATE       = 0x08000000
	}


	#region EnumWindows
	/// <summary>
	/// EnumWindows wrapper for .NET
	/// </summary>
	public class EnumWindows
	{
		#region Delegates
		private delegate int EnumWindowsProc(IntPtr hwnd, int lParam);
		#endregion

		#region UnManagedMethods
		private class UnManagedMethods
		{
			[DllImport("user32")]
			public extern static int EnumWindows (
				EnumWindowsProc lpEnumFunc, 
				int lParam);
			[DllImport("user32")]
			public extern static int EnumChildWindows (
				IntPtr hWndParent,
				EnumWindowsProc lpEnumFunc, 
				int lParam);
		}
		#endregion

		#region Member Variables
		private EnumWindowsCollection items = null;
		#endregion

		/// <summary>
		/// Returns the collection of windows returned by
		/// GetWindows
		/// </summary>
		public EnumWindowsCollection Items
		{
			get
			{
				return this.items;
			}
		}

		/// <summary>
		/// Gets all top level windows on the system.
		/// </summary>
		public void GetWindows()
		{
			this.items = new EnumWindowsCollection();
			UnManagedMethods.EnumWindows(
				new EnumWindowsProc(this.WindowEnum),
				0);
		}
		/// <summary>
		/// Gets all child windows of the specified window
		/// </summary>
		/// <param name="hWndParent">Window Handle to get children for</param>
		public void GetWindows(
			IntPtr hWndParent)
		{
			this.items = new EnumWindowsCollection();
			UnManagedMethods.EnumChildWindows(
				hWndParent,
				new EnumWindowsProc(this.WindowEnum),
				0);
		}

		#region EnumWindows callback
		/// <summary>
		/// The enum Windows callback.
		/// </summary>
		/// <param name="hWnd">Window Handle</param>
		/// <param name="lParam">Application defined value</param>
		/// <returns>1 to continue enumeration, 0 to stop</returns>
		private int WindowEnum(
			IntPtr hWnd,
			int lParam)
		{
			if (this.OnWindowEnum(hWnd))
			{
				return 1;
			}
			else
			{
				return 0;
			}
		}
		#endregion

		/// <summary>
		/// Called whenever a new window is about to be added
		/// by the Window enumeration called from GetWindows.
		/// If overriding this function, return true to continue
		/// enumeration or false to stop.  If you do not call
		/// the base implementation the Items collection will
		/// be empty.
		/// </summary>
		/// <param name="hWnd">Window handle to add</param>
		/// <returns>True to continue enumeration, False to stop</returns>
		protected virtual bool OnWindowEnum(
			IntPtr hWnd)
		{
			items.Add(hWnd);
			return true;
		}

		#region Constructor, Dispose
		public EnumWindows()
		{
			// nothing to do
		}
		#endregion
	}	
	#endregion EnumWindows

	#region EnumWindowsCollection
	/// <summary>
	/// Holds a collection of Windows returned by GetWindows.
	/// </summary>
	public class EnumWindowsCollection : ReadOnlyCollectionBase
	{
		/// <summary>
		/// Add a new Window to the collection.  Intended for
		/// internal use by EnumWindows only.
		/// </summary>
		/// <param name="hWnd">Window handle to add</param>
		public void Add(IntPtr hWnd)
		{
			EnumWindowsItem item = new EnumWindowsItem(hWnd);
			this.InnerList.Add(item);
		}

		/// <summary>
		/// Gets the Window at the specified index
		/// </summary>
		public EnumWindowsItem this[int index]
		{
			get
			{
				return (EnumWindowsItem)this.InnerList[index];
			}
		}

		/// <summary>
		/// Constructs a new EnumWindowsCollection object.
		/// </summary>
		public EnumWindowsCollection()
		{
			// nothing to do
		}
	}
	#endregion		

	#region EnumWindowsItem
	/// <summary>
	/// Provides details about a Window returned by the 
	/// enumeration
	/// </summary>
	public class EnumWindowsItem
	{
		#region Structures
		[StructLayout(LayoutKind.Sequential, Pack = 4)]
		private struct RECT
		{
			public int Left;
			public int Top;
			public int Right;
			public int Bottom;
		}
		[StructLayout(LayoutKind.Sequential, Pack = 4)]
		private struct FLASHWINFO
		{
			public int cbSize;
			public IntPtr hwnd;
			public int dwFlags;
			public int uCount;
			public int dwTimeout;
		}
		#endregion

		#region UnManagedMethods
		private class UnManagedMethods
		{
			[DllImport("user32")]
			public extern static int IsWindowVisible (
				IntPtr hWnd);
			[DllImport("user32", CharSet = CharSet.Auto)]
			public extern static int GetWindowText(
				IntPtr hWnd, 
				StringBuilder lpString, 
				int cch);
			[DllImport("user32", CharSet = CharSet.Auto)]
			public extern static int GetWindowTextLength(
				IntPtr hWnd);
			[DllImport("user32")]
			public extern static int BringWindowToTop (IntPtr hWnd);
			[DllImport("user32")]
			public extern static int SetForegroundWindow (IntPtr hWnd);
			[DllImport("user32")]
			public extern static int IsIconic(IntPtr hWnd);
			[DllImport("user32")]
			public extern static int IsZoomed(IntPtr hwnd);
			[DllImport("user32", CharSet = CharSet.Auto)]
			public extern static int GetClassName (
				IntPtr hWnd, 
				StringBuilder lpClassName, 
				int nMaxCount);
			[DllImport("user32")]
			public extern static int FlashWindow (
				IntPtr hWnd,
				ref FLASHWINFO pwfi);
			[DllImport("user32")]
			public extern static int GetWindowRect (
				IntPtr hWnd, 
				ref RECT lpRect);
			[DllImport("user32", CharSet = CharSet.Auto)]
			public extern static int SendMessage(
				IntPtr hWnd, 
				int wMsg, 
				IntPtr wParam, 
				IntPtr lParam);
			[DllImport("user32", CharSet = CharSet.Auto)]
			public extern static uint GetWindowLong (
				IntPtr hwnd, 
				int nIndex);
			public const int WM_COMMAND = 0x111;
			public const int WM_SYSCOMMAND = 0x112;
				
			public const int SC_RESTORE = 0xF120;
			public const int SC_CLOSE = 0xF060;
			public const int SC_MAXIMIZE = 0xF030;
			public const int SC_MINIMIZE = 0xF020;

			public const int GWL_STYLE = (-16);
			public const int GWL_EXSTYLE = (-20);

			/// <summary>
			/// Stop flashing. The system restores the window to its original state.
			/// </summary>
			public const int FLASHW_STOP = 0;
			/// <summary>
			/// Flash the window caption. 
			/// </summary>
			public const int FLASHW_CAPTION = 0x00000001;
			/// <summary>
			/// Flash the taskbar button.
			/// </summary>
			public const int FLASHW_TRAY = 0x00000002;
			/// <summary>
			/// Flash both the window caption and taskbar button.
			/// </summary>
			public const int FLASHW_ALL = (FLASHW_CAPTION | FLASHW_TRAY);
			/// <summary>
			/// Flash continuously, until the FLASHW_STOP flag is set.
			/// </summary>
			public const int FLASHW_TIMER = 0x00000004;
			/// <summary>
			/// Flash continuously until the window comes to the foreground. 
			/// </summary>
			public const int FLASHW_TIMERNOFG = 0x0000000C;
		}
		#endregion

		/// <summary>
		/// The window handle.
		/// </summary>
		private IntPtr hWnd = IntPtr.Zero;

		/// <summary>
		/// To allow items to be compared, the hash code
		/// is set to the Window handle, so two EnumWindowsItem
		/// objects for the same Window will be equal.
		/// </summary>
		/// <returns>The Window Handle for this window</returns>
		public override System.Int32 GetHashCode()
		{
			return (System.Int32)this.hWnd;
		}

		/// <summary>
		/// Gets the window's handle
		/// </summary>
		public IntPtr Handle
		{
			get
			{
				return this.hWnd;
			}
		}

		/// <summary>
		/// Gets the window's title (caption)
		/// </summary>
		public string Text
		{
			get
			{
				StringBuilder title = new StringBuilder(260, 260);
				UnManagedMethods.GetWindowText(this.hWnd, title, title.Capacity);
				return title.ToString();
			}
		}

		/// <summary>
		/// Gets the window's class name.
		/// </summary>
		public string ClassName
		{
			get
			{
				StringBuilder className = new StringBuilder(260, 260);
				UnManagedMethods.GetClassName(this.hWnd, className, className.Capacity);
				return className.ToString();
			}
		}

		/// <summary>
		/// Gets/Sets whether the window is iconic (mimimised) or not.
		/// </summary>
		public bool Iconic
		{
			get
			{
				return ((UnManagedMethods.IsIconic(this.hWnd) == 0) ? false : true);
			}
			set
			{
				UnManagedMethods.SendMessage(
					this.hWnd, 
					UnManagedMethods.WM_SYSCOMMAND, 
					(IntPtr)UnManagedMethods.SC_MINIMIZE,
					IntPtr.Zero);
			}
		}
			
		/// <summary>
		/// Gets/Sets whether the window is maximised or not.
		/// </summary>
		public bool Maximised
		{
			get
			{
				return ((UnManagedMethods.IsZoomed(this.hWnd) == 0) ? false : true);
			}
			set
			{
				UnManagedMethods.SendMessage(
					this.hWnd,
					UnManagedMethods.WM_SYSCOMMAND, 
					(IntPtr)UnManagedMethods.SC_MAXIMIZE,
					IntPtr.Zero);
			}
		}

		/// <summary>
		/// Gets whether the window is visible.
		/// </summary>
		public bool Visible
		{
			get
			{
				return ((UnManagedMethods.IsWindowVisible(this.hWnd) == 0) ? false : true);
			}
		}

		/// <summary>
		/// Gets the bounding rectangle of the window
		/// </summary>
		public System.Drawing.Rectangle Rect
		{
			get
			{
				RECT rc = new RECT();
				UnManagedMethods.GetWindowRect(
					this.hWnd,
					ref rc);
				System.Drawing.Rectangle rcRet = new System.Drawing.Rectangle(
					rc.Left, rc.Top,
					rc.Right - rc.Left, rc.Bottom - rc.Top);
				return rcRet;
			}
		}

		/// <summary>
		/// Gets the location of the window relative to the screen.
		/// </summary>
		public System.Drawing.Point Location
		{
			get
			{
				System.Drawing.Rectangle rc = Rect;
				System.Drawing.Point pt = new System.Drawing.Point(
					rc.Left,
					rc.Top);
				return pt;
			}
		}
			
		/// <summary>
		/// Gets the size of the window.
		/// </summary>
		public System.Drawing.Size Size
		{
			get
			{
				System.Drawing.Rectangle rc = Rect;
				System.Drawing.Size sz = new System.Drawing.Size(
					rc.Right - rc.Left,
					rc.Bottom - rc.Top);
				return sz;
			}
		}

		/// <summary>
		/// Restores and Brings the window to the front, 
		/// assuming it is a visible application window.
		/// </summary>
		public void Restore()
		{
			if (Iconic)
			{
				UnManagedMethods.SendMessage(
					this.hWnd, 
					UnManagedMethods.WM_SYSCOMMAND, 
					(IntPtr)UnManagedMethods.SC_RESTORE, 
					IntPtr.Zero);
			}
			UnManagedMethods.BringWindowToTop(this.hWnd);
			UnManagedMethods.SetForegroundWindow(this.hWnd);
		}

		public WindowStyleFlags WindowStyle
		{
			get
			{
				return (WindowStyleFlags)UnManagedMethods.GetWindowLong(
					this.hWnd, UnManagedMethods.GWL_STYLE);
			}
		}
		
		public ExtendedWindowStyleFlags ExtendedWindowStyle
		{
			get
			{
				return (ExtendedWindowStyleFlags)UnManagedMethods.GetWindowLong(
					this.hWnd, UnManagedMethods.GWL_EXSTYLE);
			}
		}

		/// <summary>
		///  Constructs a new instance of this class for
		///  the specified Window Handle.
		/// </summary>
		/// <param name="hWnd">The Window Handle</param>
		public EnumWindowsItem(IntPtr hWnd)
		{
			this.hWnd = hWnd;
		}
	}
	#endregion
}
